home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Environments / Clean 1.2.4 / IO Examples / Mines / Mines.icl next >
Encoding:
Text File  |  1997-04-25  |  16.5 KB  |  499 lines  |  [TEXT/3PRM]

  1. module Mines
  2.  
  3. /*    The game 'MacMines' in Concurrent Clean.
  4.     This program requires the 0.8 I/O library.
  5.     Run the program using the "No Console" option (Application options).
  6. */
  7.  
  8. import StdInt, StdMisc, StdBool, StdString, StdArray, StdList, StdTuple
  9. import deltaEventIO, deltaMenu, deltaTimer, deltaDialog, deltaSystem
  10. import MinesBest, Help
  11.  
  12. ::    *Mines
  13.     =    {    minefield    :: Minefield
  14.         ,    nr_visible    :: NrVisible
  15.         ,    pebbles        :: Pebbles
  16.         ,    nr_mines    :: NrMines
  17.         ,    dimension    :: Dimension
  18.         ,    time        :: Time
  19.         ,    best        :: MinesBest
  20.         ,    font        :: Font
  21.         ,    seed        :: RandomSeed
  22.         }
  23. ::    NrMines                :== Int
  24. ::    NrVisible            :== Int
  25.  
  26. ::    *IO                    :== IOState Mines
  27.  
  28.  
  29. MinesID            :== 1
  30. NewGameID            :== 10
  31. HelpID                :== 11
  32. BestTimesID            :== 12
  33. QuitID                :== 13
  34. SkillID            :== 2
  35. EasyID                :== 20
  36. InterID                :== 21
  37. HardID                :== 22
  38. CustomID            :== 23
  39.  
  40. SkillDialogID    :== 1
  41. WidthID                :== 11
  42. HeightID            :== 12
  43. NrMinesID            :== 13
  44. OkID                :== 14
  45. CancelID            :== 15
  46.  
  47. OverDlogID        :== 2
  48. NameID                :== 21
  49. OverOKID            :== 22
  50.  
  51. TimerID            :== 1
  52.  
  53. WindowID        :== 1
  54. BestWdID        :== 2
  55.  
  56. HelpFile        :== "MinesHelp"
  57. HiScoresFile    :== "mineshi"
  58.  
  59.  
  60. Start :: *World -> *World
  61. Start world
  62. #    (files, world)        = openfiles  world
  63.     (events,world)        = OpenEvents world
  64.     (aboutdialog,files)    = MakeAboutDialog "Mines" HelpFile files Help
  65.     (hifile,best)        = ReadHiScores HiScoresFile files
  66.     state0                = InitialMines EasyMines EasyDim best
  67.     (stateN,events)        = StartIO [menu,window,time,DialogSystem [aboutdialog]] state0 [InitialiseRandomSeed] events
  68.     files                = WriteHiScores hifile stateN.best
  69.     world                = closefiles  files  world
  70.     world                = CloseEvents events world
  71. =    world
  72. where
  73.     menu                = MenuSystem
  74.                             [    PullDownMenu MinesID "Mines" Able 
  75.                                 [    MenuItem NewGameID     "New Game"        (Key 'N') Able NewGame
  76.                                 ,    MenuItem BestTimesID "Best Times"    (Key 'B') Able ShowBest
  77.                                 ,    MenuSeparator
  78.                                 ,    MenuItem QuitID         "Quit"            (Key 'Q') Able Quit
  79.                                 ]
  80.                             ,    PullDownMenu SkillID "Skill" Able
  81.                                 [    MenuRadioItems EasyID
  82.                                     [    MenuRadioItem EasyID    "Easy"            (Key 'E') Able (SetGame EasyMines  EasyDim)
  83.                                     ,    MenuRadioItem InterID    "Intermediate"    (Key 'I') Able (SetGame InterMines InterDim)
  84.                                     ,    MenuRadioItem HardID    "Hard"            (Key 'H') Able (SetGame HardMines  HardDim)
  85.                                     ,    MenuRadioItem CustomID    "Custom..."        (Key 'C') Able Custom
  86.                                     ]
  87.                                 ]
  88.                             ]
  89.     window                = WindowSystem 
  90.                             [    FixedWindow WindowID (0,0) "Mines" (WindowPictDomain EasyDim) DrawGame
  91.                                     [    GoAway    Quit
  92.                                     ,    Mouse    Able PlayMines
  93.                                     ]
  94.                             ]
  95.     
  96.     time                = TimerSystem
  97.                             [    Timer TimerID Unable TicksPerSecond Timing
  98.                             ]
  99.  
  100.     InitialMines :: Int Dimension MinesBest -> Mines
  101.     InitialMines nrmines dim best
  102.                         = {    minefield    = [[]]
  103.                           ,    nr_visible    = 0
  104.                           ,    pebbles        = []
  105.                           ,    nr_mines    = nrmines
  106.                           ,    dimension    = dim
  107.                           ,    time        = Off
  108.                           ,    best        = best
  109.                           ,    font        = snd (SelectFont "Times" ["BoldStyle"] 12)
  110.                           ,    seed        = NullRandomSeed
  111.                           }
  112.     
  113.     InitialiseRandomSeed :: Mines IO -> (Mines, IO)
  114.     InitialiseRandomSeed mines=:{nr_mines,dimension} io
  115.     #    (seed,io)        = GetNewRandomSeed io
  116.         (field,seed)    = SowMines nr_mines dimension seed
  117.     =    ({mines & minefield=field,seed=seed},io)
  118.  
  119. SetMines :: Int Dimension Mines -> Mines
  120. SetMines nrmines dim mines=:{seed}
  121. #    (field,seed)    = SowMines nrmines dim seed
  122. =    {    mines    &    minefield    = field
  123.                 ,    nr_visible    = 0
  124.                 ,    pebbles        = []
  125.                 ,    nr_mines    = nrmines
  126.                 ,    dimension    = dim
  127.                 ,    time        = Off
  128.                 ,    seed        = seed
  129.     }
  130.  
  131. Help :: Mines IO -> (Mines, IO)
  132. Help mines=:{best=(files,bt)} io
  133. #    (files,io)    = ShowHelp HelpFile files io
  134. =    ({mines & best=(files,bt)},io)
  135.  
  136. Custom :: Mines IO -> (Mines, IO)
  137. Custom mines=:{pebbles,nr_mines,dimension=(col,row)} io
  138. =    OpenModalDialog dialog mines io
  139. where
  140.     dialog        = CommandDialog SkillDialogID "Custom" [] OkID
  141.                     [    StaticText     1            Left widthtext
  142.                     ,    EditText     WidthID   (RightTo 1)        (Pixel 40) 1 (toString col)
  143.                     ,    StaticText     3            Left heighttext
  144.                     ,    EditText     HeightID  (Below WidthID)    (Pixel 40) 1 (toString row)
  145.                     ,    StaticText     5            Left "Mines:"
  146.                     ,    EditText     NrMinesID (Below HeightID)    (Pixel 40) 1 (toString total)
  147.                     ,    DialogButton CancelID    Center                "Cancel"    Able Cancel
  148.                     ,    DialogButton OkID        (RightTo CancelID)    "OK"        Able OK
  149.                     ]
  150.     total        = length pebbles + nr_mines
  151.     widthtext    =  "Width ("+++toString minw+++"-"+++toString maxw+++"):"
  152.     heighttext    = "Height ("+++toString minh+++"-"+++toString maxh+++"):"
  153.     (minw,minh)    = (1,2)
  154.     (maxw,maxh)    = MaxDimension
  155.     
  156.     Cancel :: DialogInfo Mines IO -> (Mines, IO)
  157.     Cancel _ mines io = (mines, CloseActiveDialog io)
  158.     
  159.     OK :: DialogInfo Mines IO -> (Mines, IO)
  160.     OK info mines io
  161.     =    SetGame nr dim mines1 (CloseActiveDialog io)
  162.     where
  163.         width    = Between minw maxw            (toInt (GetEditText WidthID   info))
  164.         height    = Between minh maxh            (toInt (GetEditText HeightID  info))
  165.         nr        = Between 1 (width*height)    (toInt (GetEditText NrMinesID info))
  166.         dim        = (width,height)
  167.         mines1    = {mines & minefield=[],nr_visible=0,pebbles=[],nr_mines=nr,dimension=dim}
  168.         
  169.         Between :: !Int !Int !Int -> Int
  170.         Between min max n
  171.         |    n<min        = min
  172.         |    n>max        = max
  173.         |    otherwise    = n
  174.  
  175.  
  176. /*    The menu definition:
  177. */
  178.  
  179. Quit :: Mines IO -> (Mines, IO)
  180. Quit mines io = (mines, QuitIO io)
  181.  
  182. NewGame :: Mines IO -> (Mines, IO)
  183. NewGame mines=:{pebbles,nr_mines,dimension} io
  184. #    io            = ChangeUpdateFunction    WindowID DrawGame io
  185.     io            = ActivateWindow        WindowID io
  186.     io            = EnableMouse            WindowID io
  187.     mines        = SetMines (length pebbles+nr_mines) dimension mines
  188.     (mines,io)    = DrawInWindowFrame        WindowID EraseBeforeDraw mines io
  189. =    (mines,io)
  190.  
  191. ShowBest :: Mines IO -> (Mines, IO)
  192. ShowBest mines io
  193. =    (mines, OpenWindows [FixedWindow BestWdID (40,40) "Hall of Fame" ((0,0),size) UpdateBest []] io)
  194. where
  195.     size    = (BestX,BestY)
  196.     
  197.     UpdateBest :: UpdateArea Mines -> (Mines, [DrawFunction])
  198.     UpdateBest area mines=:{best=(_,hi)}
  199.     =    (mines,ShowBestTimes hi)
  200.  
  201. SetGame :: Int Dimension Mines IO -> (Mines, IO)
  202. SetGame nr dim mines io
  203. #    io            = ChangeUpdateFunction    WindowID DrawGame io
  204.     mines        = SetMines nr dim mines
  205.     (mines,io)    = ChangePictureDomain    WindowID (WindowPictDomain dim) mines io
  206.     io            = EnableMouse            WindowID io
  207.     io            = ActivateWindow        WindowID io
  208. =    (mines,io)
  209.  
  210. /*    The mouse definition:
  211. */
  212.  
  213. PlayMines :: MouseState Mines IO -> (Mines, IO)
  214. PlayMines (_,ButtonUp,_)              mines io    = (mines, io)
  215. PlayMines (_,ButtonStillDown,_)          mines io    = (mines, io)
  216. PlayMines (pos,_,mods=:(shift,_,_,_)) mines=:{dimension} io
  217. |    shift                                = PutOrGetPebble (MousePositionToPosition pos dimension) mines io
  218. |    NoMods mods                            = StepCautiously (MousePositionToPosition pos dimension) mines io
  219. |    otherwise                            = (mines,io)
  220. where
  221.     NoMods (False,False,False,False)    = True
  222.     NoMods _                            = False
  223.     
  224.     MousePositionToPosition :: Point Dimension -> Position
  225.     MousePositionToPosition (x,y) (col,row)
  226.     |    x<size*col && y<size*row    = (x/size+1,y/size+1)
  227.     |    otherwise                    = (0,0)
  228.     where
  229.         size                        = SizeArea
  230.  
  231. PutOrGetPebble :: Position Mines IO -> (Mines, IO)
  232. PutOrGetPebble (0,0) mines io
  233. =    (mines, io)
  234. PutOrGetPebble pos mines=:{pebbles,nr_mines=0} io
  235. |    isMember pos pebbles    = GetPebble pos mines io
  236. |    otherwise                = (mines, io)
  237. PutOrGetPebble pos mines=:{pebbles,time=Running _} io
  238. |    isMember pos pebbles    = GetPebble pos mines io
  239. |    otherwise                = PutPebble pos mines io
  240. PutOrGetPebble pos mines=:{time=Off} io
  241. =    PutPebble pos {mines & time=Running 0} (EnableTimer TimerID io)
  242.  
  243. PutPebble :: Position Mines IO -> (Mines, IO)
  244. PutPebble pos mines=:{minefield,nr_visible,pebbles,nr_mines} io
  245. |    not (InvisibleSpot (GetSpot pos minefield))
  246. =    (mines, io)
  247. |    otherwise
  248. =    FinalSpotRevealed mines1 io1
  249. with
  250.     mines1    = {mines & nr_visible=nr_visible+1,pebbles=[pos:pebbles],nr_mines=nr_mines-1}
  251.     io1        = DrawInWindow WindowID [DrawPebble pos] io
  252.  
  253. GetPebble :: Position Mines IO -> (Mines, IO)
  254. GetPebble pos mines=:{nr_visible,pebbles,nr_mines,dimension,font} io
  255. #    io    = DrawInWindow WindowID [DrawEmptyArea pos,DrawNrMines font (nr_mines+1) dimension] io
  256. =    ({mines & nr_visible=nr_visible-1,pebbles=RemovePebble pos pebbles,nr_mines=nr_mines+1}, io)
  257.  
  258. StepCautiously :: Position Mines IO -> (Mines, IO)
  259. StepCautiously (0,0) mines io
  260. =    (mines, io)
  261. StepCautiously pos mines=:{minefield,nr_visible,pebbles,nr_mines,dimension,time=Running _} io
  262. |    MineSpot spot                = (mines1, io2)
  263.                                 with
  264.                                     io1            = ChangeIOState
  265.                                                     [    ChangeUpdateFunction WindowID DrawFinalGame
  266.                                                     ,    DisableTimer         TimerID
  267.                                                     ,    DisableMouse         WindowID
  268.                                                     ]    io
  269.                                     (mines1,io2)= DrawInWindowFrame WindowID DrawFinalGame mines io1
  270. |    not (InvisibleSpot spot)    = (mines, io)
  271. |    NulSpot spot                = FinalSpotRevealed safe_mines rev_io
  272.                                 with
  273.                                     (less_pebbles,safe_minefield,nr_revealed_spots,drawfs)
  274.                                                     = RevealSafeSpots dimension pos pebbles minefield
  275.                                     nr_less_pebbles    = length less_pebbles
  276.                                     more_visible    = nr_visible+nr_revealed_spots-(nr_pebbles-nr_less_pebbles)
  277.                                     more_mines        = nr_pebbles+nr_mines-nr_less_pebbles
  278.                                     safe_mines        = {mines & minefield    = safe_minefield
  279.                                                              , nr_visible    = more_visible
  280.                                                              , pebbles        = less_pebbles
  281.                                                              , nr_mines        = more_mines
  282.                                                       }
  283.                                     rev_io            = DrawInWindow WindowID drawfs io
  284. |    otherwise                    = FinalSpotRevealed spot_mines spot_io
  285.                                 with
  286.                                     (spot`,spot_minefield)
  287.                                                     = RevealSpot pos minefield
  288.                                     nr_pebbles_less    = length one_pebble_less
  289.                                     one_more_mine    = nr_mines+nr_pebbles-nr_pebbles_less
  290.                                     one_pebble_less    = RemovePebble pos pebbles
  291.                                     one_more_visible= nr_visible+1+nr_pebbles_less-nr_pebbles
  292.                                     spot_mines        = {mines & minefield    = spot_minefield
  293.                                                              , nr_visible    = one_more_visible
  294.                                                              , pebbles        = one_pebble_less
  295.                                                              , nr_mines        = one_more_mine
  296.                                                       }
  297.                                     spot_io            = DrawInWindow WindowID [DrawSpot pos spot`] io
  298. where
  299.     spot        = GetSpot pos minefield
  300.     nr_pebbles    = length pebbles
  301. StepCautiously pos mines=:{time=Off} io
  302. =    StepCautiously pos {mines & time=Running 0} (EnableTimer TimerID io)
  303.  
  304. FinalSpotRevealed :: Mines IO -> (Mines, IO)
  305. FinalSpotRevealed mines=:{nr_visible,nr_mines,dimension,font} io
  306. |    nr_mines==0 && nr_visible==fst dimension*snd dimension
  307. =    CheckForBestTime mines1 io2
  308. with
  309.     io1            = ChangeIOState
  310.                     [    ChangeUpdateFunction WindowID DrawFinalGame
  311.                     ,    DisableTimer         TimerID
  312.                     ,    DisableMouse         WindowID
  313.                     ]    io
  314.     (mines1,io2)= DrawInWindowFrame WindowID DrawFinalGame mines io1
  315.     
  316.     CheckForBestTime :: Mines IO -> (Mines, IO)
  317.     CheckForBestTime mines=:{pebbles,dimension,time,best=(_,hi)} io
  318.     |    not (ItsABestTime (length pebbles) dimension time hi)
  319.     =    (mines,io)
  320.     |    otherwise
  321.     =    OpenModalDialog dialog mines io
  322.     with
  323.         dialog    = CommandDialog OverDlogID "Game Over" [] OverOKID
  324.                     [    StaticText     1            Left        "Game Over with a new best time!"
  325.                     ,    StaticText     2            Left        "Your name:"
  326.                     ,    EditText     NameID        (RightTo 2) (MM 40.0) 1 ""
  327.                     ,    DialogButton OverOKID    Center        "OK" Able Ok
  328.                     ]
  329.         
  330.         Ok :: DialogInfo Mines IO -> (Mines, IO)
  331.         Ok info mines=:{pebbles,dimension,time,best=(files,hi)} io
  332.         #    io            = CloseActiveDialog io
  333.         |    name==""    = (mines, io)
  334.         #    io            = DrawInWindow BestWdID (ShowBestTimes newhi) io
  335.         |    otherwise    = ({mines & best=(files,newhi)}, io)
  336.         where
  337.             newhi        = AddBestTime (LimitString 16 name) (length pebbles) dimension time hi
  338.             name        = GetEditText NameID info
  339. |    otherwise
  340. =    (mines,DrawInWindow WindowID [DrawNrMines font nr_mines dimension] io)
  341.  
  342. DrawFinalGame :: UpdateArea Mines -> (Mines, [DrawFunction])
  343. DrawFinalGame upd_area mines=:{minefield,pebbles,nr_mines,dimension,time,font}
  344. =    (    mines
  345.     ,    [    SetFont        font
  346.         ,    DrawGrid                        dimension
  347.         ,    DrawNrMines font nr_mines        dimension
  348.         ,    DrawTime    font (GetTime time) dimension
  349.         :    MapMinefield DrawAnySpot minefield
  350.         ++    MapMinefield (DrawCorrectnessPebble pebbles) minefield
  351.         ]
  352.     )
  353.  
  354. /*    The update definition:
  355. */
  356.  
  357. DrawGame :: UpdateArea Mines -> (Mines, [DrawFunction])
  358. DrawGame upd_area mines=:{minefield,pebbles,nr_mines,dimension,time,font,seed}
  359. =    (    mines
  360.     ,    [    SetFont        font
  361.         ,    DrawGrid                        dimension
  362.         ,    DrawNrMines    font nr_mines        dimension
  363.         ,    DrawTime    font (GetTime time)    dimension
  364.         :    MapMinefield DrawSpot minefield
  365.         ++    map DrawPebble pebbles
  366.         ]
  367.     )
  368.  
  369. EraseBeforeDraw :: UpdateArea Mines -> (Mines, [DrawFunction])
  370. EraseBeforeDraw updarea mines
  371. #    (mines,drawfs)    = DrawGame updarea mines
  372. =    (mines,map EraseRectangle updarea ++ drawfs)
  373.  
  374.  
  375. /*    The timer definition:
  376. */
  377.  
  378. Timing :: TimerState Mines IO -> (Mines, IO)
  379. Timing passed mines=:{dimension,time=Running last,font} io
  380. =    ({mines & time=Running now}, DrawInWindow WindowID [DrawTime font now dimension] io)
  381. where
  382.     now    = last+passed
  383. Timing _ mines io
  384. =    (mines, io)
  385.  
  386.  
  387. //    Reveal all newly discovered spots:
  388. RevealSafeSpots :: !Dimension !Position Pebbles Minefield -> (Pebbles,Minefield,Int,![DrawFunction])
  389. RevealSafeSpots dim pos pebbles minefield
  390. #    (spot,minefield)    = RevealSpot pos minefield
  391.     (pebbles,minefield,revealed_spots,drawfs)
  392.                         = RevealAreas dim pos Compass (RemovePebble pos pebbles) minefield [[(pos,spot)]]
  393. =    (pebbles,minefield,AreaLength revealed_spots,[DrawSpot pos spot:drawfs])
  394. where
  395.     RevealAreas    :: !Dimension !Position ![Vector] Pebbles Minefield [[(Position,Spot)]]
  396.                 -> (Pebbles,Minefield,[[(Position,Spot)]],![DrawFunction])
  397.     RevealAreas dim pos [v:vs] pebbles minefield done
  398.     =    (pebbles2,minefield2,done2,drawfs1++drawfs2)
  399.     where
  400.         (pebbles1,minefield1,done1,drawfs1)    = RevealArea  dim (TranslatePoint v pos) v pebbles minefield done
  401.         (pebbles2,minefield2,done2,drawfs2)    = RevealAreas dim pos vs pebbles1 minefield1 done1
  402.     RevealAreas _ _ _ pebbles minefield done 
  403.     =    (pebbles,minefield,done,[])
  404.     
  405.     RevealArea    :: !Dimension !Position Vector Pebbles Minefield [[(Position,Spot)]]
  406.                 -> (Pebbles,Minefield,[[(Position,Spot)]],![DrawFunction])
  407.     RevealArea dim pos v pebbles minefield done
  408.     |    not (InMinefield dim pos) || not (UniqueArea pos done) || not (InvisibleSpot spot`)
  409.     =    (pebbles,minefield,done,[])
  410.     |    NulSpot spot
  411.     =    (pebbles``,minefield``,done``,[DrawSpot pos spot:drawfs``])
  412.     with
  413.         (pebbles``,minefield``,done``,drawfs``)    = RevealAreas dim pos vs pebbles` minefield` done`
  414.     |    otherwise
  415.     =    (pebbles`,minefield`,done`,[DrawSpot pos spot])
  416.     where
  417.         (spot,minefield`)    = RevealSpot pos minefield
  418.         done`                = AddArea pos spot done
  419.         pebbles`            = RemovePebble pos pebbles
  420.         vs                    = NextCompass v
  421.         spot`                = GetSpot pos minefield
  422.         
  423.         InMinefield :: !Dimension !Position -> Bool
  424.         InMinefield (col,row) (x,y)
  425.         |    x==0 || y==0    = False
  426.         |    otherwise        = x<=col && y<=row
  427.         
  428.         NextCompass :: !Vector -> [Vector]
  429.         NextCompass v=:(x,y)
  430.         |    x==0            = [v,(y,0),(~y,0),(1,y),(-1,y)]
  431.         |    y==0            = [v,(0,x),(0,~x),(x,1),(x,-1)]
  432.         |    otherwise        = [v,(x,0),(0,y),(x,~y),(~x,y)]
  433.         
  434.         UniqueArea :: !Position ![[(Position,Spot)]] -> Bool
  435.         UniqueArea mine=:(x,_) [col_areas=:[area=:((x`,_),_):_]:areas]
  436.         |    x<x`        = True
  437.         |    x==x`        = UniqueColArea mine col_areas
  438.                         with
  439.                             UniqueColArea :: !Position ![(Position,Spot)] -> Bool
  440.                             UniqueColArea mine=:(_,y) [((_,y`),_):areas]
  441.                             |    y<y`        = True
  442.                             |    y==y`        = False
  443.                             |    otherwise    = UniqueColArea mine areas
  444.                             UniqueColArea _ _
  445.                             =    True
  446.         |    otherwise    = UniqueArea mine areas
  447.         UniqueArea mine [[] : areas]
  448.         =    UniqueArea mine areas
  449.         UniqueArea _ _
  450.         =    True
  451.         
  452.         AddArea :: !Position Spot ![[(Position,Spot)]] -> [[(Position,Spot)]]
  453.         AddArea pos=:(x,_) spot [col_areas=:[area=:((x`,_),_):_]:areas]
  454.         |    x<x`        = [[(pos,spot)], col_areas : areas]
  455.         |    x==x`        = [AddColArea pos spot col_areas : areas]
  456.                         with
  457.                             AddColArea :: Position Spot [(Position, Spot)] -> [(Position, Spot)]
  458.                             AddColArea pos=:(_,y) spot [area=:((_,y`),_):areas]
  459.                             |    y<y`        = [(pos,spot),area:areas]
  460.                             |    y==y`        = [area:areas]
  461.                             |    otherwise    = [area:AddColArea pos spot areas]
  462.                             AddColArea pos spot _
  463.                             =    [(pos,spot)]
  464.         |    otherwise    = [col_areas:AddArea pos spot areas]
  465.         AddArea pos spot [[]:areas]
  466.         =    AddArea pos spot areas
  467.         AddArea pos spot []
  468.         =    [[(pos,spot)]]
  469.     
  470.     AreaLength :: ![[x]] -> Int
  471.     AreaLength [list:lists]        = length list+AreaLength lists 
  472.     AreaLength _                = 0
  473.  
  474. Compass    :== [(-1,-1), (0,-1), (1,-1), (1,0), (1,1), (0,1), (-1,1), (-1,0)]
  475.  
  476.  
  477. //    Map a Spot at Position drawing function over the minefield:
  478.  
  479. ::    DrawSpotFunction    :== Position -> Spot -> DrawFunction 
  480.  
  481. MapMinefield :: !DrawSpotFunction !Minefield -> [DrawFunction]
  482. MapMinefield f minefield
  483. =    flatten (snd (smap (MapColMinefield f) 1 minefield))
  484. where
  485.     MapColMinefield :: !DrawSpotFunction !Int ![Spot] -> (!Int,![DrawFunction])
  486.     MapColMinefield f col col_mines
  487.     =    (col+1,snd (smap (MapCol f) (col,1) col_mines))
  488.     where
  489.         MapCol :: !DrawSpotFunction !Position !Spot -> (!Position,!DrawFunction)
  490.         MapCol f pos=:(col,row) spot
  491.         =    ((col,row+1),f pos spot)
  492.     
  493.     smap :: !(!.s -> .x -> (!.s,.y)) !.s ![.x] -> (!.s,![.y])
  494.     smap f s [x:xs]
  495.     #    (s,y)    = f s x
  496.         (s,ys)    = smap f s xs
  497.     =    (s,[y:ys])
  498.     smap _ s _    = (s,[])
  499.